package ru.spb.osll.myfit.server.comparing;

import ru.spb.osll.myfit.common.ComparisonResult;
import ru.spb.osll.myfit.common.Range;
import ru.spb.osll.myfit.common.chart.ChartCurve;
import ru.spb.osll.myfit.common.chart.ChartData;
import ru.spb.osll.myfit.common.chart.ChartPoint;
import ru.spb.osll.myfit.server.chart.ChartHandler;

import java.util.ArrayList;
import java.util.List;

public class Mean implements ChartComparator {
  private ChartHandler m_chartHandler = new ChartHandler();

  public Mean(ChartHandler chartHandler) {
    m_chartHandler = chartHandler;
  }

  @Override
  public String id() {
    return "Compare by mean";
  }

  @Override
  public ComparisonResult compare(List<String> ids, Range range) {
    ComparisonResult comparisonResult = new ComparisonResult();
    List<Double> means = new ArrayList<Double>();

    List<ChartData> ChartDataList = m_chartHandler.getChartDataList(ids);
    for (ChartData ChartData : ChartDataList) {
      //todo: rewrite next line with appropriate code
      ChartCurve curve = ChartData.getCurves().get(0);
      int first = findLastLesserIndex(curve.getPoints(), range.getStart());
      int last = findFirstBiggerIndex(curve.getPoints(), range.getStart() + range.getLength());

      double firstValue = getIntermediateValue(range.getStart(), curve, first);

      double lastValue = getIntermediateValue(range.getStart() + range.getLength(), curve, last);

      double sum = getSquare(range.getStart(), curve.getPoints().get(first + 1).getX(), firstValue, curve.getPoints().get(first + 1).getY()) +
              getSquare(curve.getPoints().get(last - 1).getX(), range.getStart() + range.getLength(), curve.getPoints().get(last - 1).getY(), lastValue);

      for (int i = first + 1; i < last - 1; ++i) {
        sum += getSquare(curve.getPoints().get(i).getX(), curve.getPoints().get(i + 1).getX(),
                curve.getPoints().get(i).getY(), curve.getPoints().get(i + 1).getY());
      }

      means.add(sum / range.getLength());

      comparisonResult.getInfo().add("Mean: " + Double.toString(sum / range.getLength()));
    }

    comparisonResult.getInfo().add("Performance boost: " + Double.toString(means.get(1) / means.get(0)));

    return comparisonResult;
  }

  private double getSquare(double x, double y, double xValue, double yValue) {
    return (xValue + yValue) / 2 * (y - x);
  }

  private double getIntermediateValue(double x, ChartCurve curve, int first) {
    return curve.getPoints().get(first).getY() +
            (curve.getPoints().get(first + 1).getY() - curve.getPoints().get(first).getY()) /
                    (curve.getPoints().get(first + 1).getX() - curve.getPoints().get(first).getX())
                    * (x - curve.getPoints().get(first).getX()) /
                    (curve.getPoints().get(first + 1).getX() - curve.getPoints().get(first).getX());
  }

  private int findFirstBiggerIndex(List<ChartPoint> points, double end) {
    int step = (int) (points.get(5).getX() - points.get(4).getX() + 0.5);
    int x = (int) (end / step - 1.5);
    if (x < 0) {
      x = 0;
    }
    for (int i = x; i < points.size(); ++i) {
      if (points.get(i).getX() > end)
        return i;
    }
    return points.size() - 2;
  }

  private int findLastLesserIndex(List<ChartPoint> points, double start) {
    int step = (int) (points.get(5).getX() - points.get(4).getX() + 0.5);
    int x = (int) (start / step - 1.5);
    if (x < 0) {
      x = 0;
    }
    for (int i = x; i < points.size(); ++i) {
      if (points.get(i).getX() > start)
        return i - 1;
    }
    return 0;
  }
}
